/*
 * Copyright (c) 2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "distributed_input_test.h"

#include <cstring>
#include <thread>

#include <unistd.h>

#include "idistributed_hardware_source.h"
#include "ipc_skeleton.h"
#include "nlohmann/json.hpp"
#include "linux/input-event-codes.h"

#include "softbus_bus_center.h"

namespace OHOS {
namespace DistributedHardware {
namespace DistributedInput {
namespace {
    const std::string DINPUT_PKG_NAME = "ohos.dhardware.dinput";
    const int32_t TEST_GET_DEVICE_INFO = 1;
    const int32_t TEST_PREPARE_REMOTE = 2;
    const int32_t TEST_START_REMOTE = 3;
    const int32_t TEST_STOP_REMOTE = 4;
    const int32_t TEST_UN_PREPARE_REMOTE = 5;
    const int32_t TEST_IS_START_INPUT = 6;
    const int32_t TEST_IS_NEED_FILTER_OUT = 7;
    const int32_t TEST_HELP = 8;

    const int32_t TYPE_MOUSE_TOUCH_PAD = 0x0001;
    const int32_t TYPE_KEY_BOARD = 0x0002;
    const int32_t TYPE_ALL = TYPE_MOUSE_TOUCH_PAD | TYPE_KEY_BOARD;

    BusinessEvent eventCtrlAltDel_1 = { { KEY_LEFTCTRL, KEY_LEFTALT }, KEY_DELETE, KEY_DOWN };
    BusinessEvent eventCtrlAltDel_2 = { { KEY_LEFTCTRL, KEY_RIGHTALT }, KEY_DELETE, KEY_DOWN };
    BusinessEvent eventCtrlAltDel_3 = { { KEY_RIGHTCTRL, KEY_LEFTALT }, KEY_DELETE, KEY_DOWN };
    BusinessEvent eventCtrlAltDel_4 = { { KEY_RIGHTCTRL, KEY_RIGHTALT }, KEY_DELETE, KEY_DOWN };

    BusinessEvent eventCtrlAltDel_11 = { { KEY_LEFTALT, KEY_LEFTCTRL }, KEY_DELETE, KEY_DOWN };
    BusinessEvent eventCtrlAltDel_21 = { { KEY_RIGHTALT, KEY_LEFTCTRL }, KEY_DELETE, KEY_DOWN };
    BusinessEvent eventCtrlAltDel_31 = { { KEY_LEFTALT, KEY_RIGHTCTRL }, KEY_DELETE, KEY_DOWN };
    BusinessEvent eventCtrlAltDel_41 = { { KEY_RIGHTALT, KEY_RIGHTCTRL }, KEY_DELETE, KEY_DOWN };

    BusinessEvent eventAltF4_1 = { { KEY_LEFTALT }, KEY_F4, KEY_DOWN };
    BusinessEvent eventAltF4_2 = { { KEY_RIGHTALT }, KEY_F4, KEY_DOWN };

    BusinessEvent eventCtrlC_1 = { { KEY_LEFTCTRL }, KEY_C, KEY_DOWN };
    BusinessEvent eventCtrlC_2 = { { KEY_RIGHTCTRL }, KEY_C, KEY_DOWN };
    BusinessEvent eventCtrlV_1 = { { KEY_RIGHTCTRL }, KEY_V, KEY_DOWN };
    BusinessEvent eventCtrlV_2 = { { KEY_RIGHTCTRL }, KEY_V, KEY_DOWN };
    BusinessEvent eventABCDE = { { KEY_A, KEY_B, KEY_C, KEY_D }, KEY_E, KEY_DOWN };
    BusinessEvent eventM = { {}, KEY_M, KEY_DOWN };

    const int32_t MAX_COMB_KEY_NUM = 16;

    // { cmdId, { combKeyName, isInWhiteList } }
    std::map<int32_t, std::pair<std::string, bool>> combKeys = {
        {1, { "Left_Ctrl + Left_Alt + Del", true } },
        {2, { "Left_Ctrl + Right_Alt + Del", true } },
        {3, { "Right_Ctrl + Left_Alt + Del", true } },
        {4, { "Right_Ctrl + Right_Alt + Del", true } },
        {5, { "Left_Alt + Left_Ctrl + Del", true } },
        {6, { "Right_Alt + Left_Ctrl + Del", true } },
        {7, { "Left_Alt + Right_Ctrl + Del", true } },
        {8, { "Right_Alt + Right_Ctrl + Del", true } },
        {9, { "Left_Alt + F4", true } },
        {10, { "Right_Alt + F4", true } },
        {11, { "Left_Ctrl + C", false } },
        {12, { "Right_Ctrl + C", false } },
        {13, { "Left_Ctrl + V", false } },
        {14, { "Right_Ctrl + V", false } },
        {15, { "A + B + C + D + E", false } },
        {16, { "M", false } },
    };

    std::map<int32_t, BusinessEvent> events = {
        {1, eventCtrlAltDel_1 },
        {2, eventCtrlAltDel_2 },
        {3, eventCtrlAltDel_3 },
        {4, eventCtrlAltDel_4 },
        {5, eventCtrlAltDel_11 },
        {6, eventCtrlAltDel_21 },
        {7, eventCtrlAltDel_31 },
        {8, eventCtrlAltDel_41 },
        {9, eventAltF4_1 },
        {10, eventAltF4_2 },
        {11, eventCtrlC_1 },
        {12, eventCtrlC_2 },
        {13, eventCtrlV_1 },
        {14, eventCtrlV_2 },
        {15, eventABCDE },
        {16, eventM }
    };
}

void DistributedInputTest::PrintDeviceTypeCmd()
{
    std::cout << "You can input" << TYPE_MOUSE_TOUCH_PAD << " ~ " << TYPE_ALL << std::endl;
    std::cout << TYPE_MOUSE_TOUCH_PAD << ":mouse|touchpad " << std::endl;
    std::cout << TYPE_KEY_BOARD << ":keyboard " << std::endl;
    std::cout << TYPE_ALL << ":all " << std::endl;
}

void DistributedInputTest::PrintIsNeedFilterOutCmd()
{
    std::cout << "combination key list: (select by CmdId)" << std::endl;
    for (int32_t cmdId = 1; cmdId <= MAX_COMB_KEY_NUM; cmdId++) {
        std::cout << "CmdId: " << cmdId << " -> " << combKeys[cmdId].first
            << ", IsInWhiteList: " << combKeys[cmdId].second << std::endl;
    }
}

void DistributedInputTest::TestPrepareDInputCallback::OnResult(
    const std::string &deviceId, const int32_t &status)
{
    std::cout << std::endl;
    std::cout << "IPrepareDInputCallback::OnResult " << std::endl;
    std::cout << "deviceId::" << deviceId << std::endl;
    std::cout << "status::" << status << std::endl;
    std::cout << "Enter the case No : " << std::endl;
    return;
}

void DistributedInputTest::TestUnprepareDInputCallback::OnResult(
    const std::string &deviceId, const int32_t &status)
{
    std::cout << std::endl;
    std::cout << "IUnprepareDInputCallback::OnResult " << std::endl;
    std::cout << "deviceId::" << deviceId << std::endl;
    std::cout << "status::" << status << std::endl;
    std::cout << "Enter the case No : " << std::endl;
    return;
}

void DistributedInputTest::TestStartDInputCallback::OnResult(
    const std::string& deviceId, const uint32_t& inputTypes, const int32_t& status)
{
    std::cout << std::endl;
    std::cout << "IStartDInputCallback::OnResult " << std::endl;
    std::cout << "deviceId::" << deviceId << std::endl;
    std::cout << "inputTypes::" << inputTypes << std::endl;
    std::cout << "status::" << status << std::endl;
    std::cout << "Enter the case No : " << std::endl;
    return;
}

void DistributedInputTest::TestStopDInputCallback::OnResult(
    const std::string& deviceId, const uint32_t& inputTypes, const int32_t& status)
{
    std::cout << std::endl;
    std::cout << "TestStopDInputCallback::OnResult " << std::endl;
    std::cout << "deviceId::" << deviceId << std::endl;
    std::cout << "inputTypes::" << inputTypes << std::endl;
    std::cout << "status::" << status << std::endl;
    std::cout << "Enter the case No : " << std::endl;
    return;
}

void DistributedInputTest::GetLocalDeviceInfo()
{
    auto info = std::make_unique<NodeBasicInfo>();
    auto ret = GetLocalNodeDeviceInfo(DINPUT_PKG_NAME.c_str(), info.get());
    if (ret != 0) {
        DHLOGE("GetLocalNodeDeviceInfo failed, errCode = %d", ret);
        return;
    }

    std::cout << "Local Device Info: " << std::endl;
    std::cout << "networkId::" << info->networkId << std::endl;
    std::cout << "deviceName:: " << info->deviceName << std::endl << std::endl;
}

void DistributedInputTest::GetDeviceInfo()
{
    std::cout << "GetDeviceInfo::start " << std::endl;

    GetLocalDeviceInfo();

    NodeBasicInfo *info = NULL;
    int32_t infoNum = 0;
    GetAllNodeDeviceInfo("ohos.distributedhardware.dinput", &info, &infoNum);

    for (int32_t i = 0; i < infoNum; i++) {
        std::cout << "--------" << " online device: " << i << "--------" << std::endl;
        std::cout << "device: " << i << std::endl;
        std::cout << "networkId::" << std::endl;
        std::cout << info->networkId << std::endl;
        std::cout << "deviceName::" << std::endl;
        std::cout << info->deviceName << std::endl;
        std::cout << "------------------------------------------------" << std::endl << std::endl;
        info++;
    }
    std::cout << "GetDeviceInfo::end " << std::endl;
}

void DistributedInputTest::IsStartDistributedInput()
{
    std::cout << "IsStartDistributedInput::start " << std::endl;

    uint32_t inputType = 0;
    std::cout << "inputType::";
    std::cin >> inputType;

    DInputServerType type = DistributedInputKit::IsStartDistributedInput(inputType);
    if (type == DInputServerType::SOURCE_SERVER_TYPE) {
        std::cout << "The input type is using in source sa! " << std::endl;
    } else if (type == DInputServerType::SINK_SERVER_TYPE) {
        std::cout << "The input type is using in sink sa! " << std::endl;
    } else {
        std::cout << "No sa is using! " << std::endl;
    }
    std::cout << "IsStartDistributedInput::end " << std::endl;
}

void DistributedInputTest::IsNeedFilterOut()
{
    std::cout << "IsNeedFilterOut::start " << std::endl;
    PrintIsNeedFilterOutCmd();

    int32_t cmdId = 0;
    std::string deviceId;

    std::cout << "DeviceId::";
    std::cin >> deviceId;

    std::cout << "CmdId::";
    std::cin >> cmdId;

    if (cmdId <= 0 || cmdId > MAX_COMB_KEY_NUM) {
        std::cout << "CmdId invalid!" << std::endl;
        return;
    }

    bool ret = DistributedInputKit::IsNeedFilterOut(deviceId, events[cmdId]);
    if (ret) {
        std::cout << " Result::true! " << std::endl;
        std::cout << "The businessEvent is in the white list!" << std::endl;
    } else {
        std::cout << " Result::false! " << std::endl;
        std::cout << "The businessEvent is not in the white list!" << std::endl;
    }

    std::cout << "IsNeedFilterOut::end " << std::endl;
}

void DistributedInputTest::PrepareRemoteInput()
{
    std::cout << "PrepareRemoteInput::start " << std::endl;

    std::string rDevId = "";

    std::cout << "devId::";
    std::cin >> rDevId;

    prepareCb = new(std::nothrow) TestPrepareDInputCallback();
    int32_t ret = DistributedInputKit::PrepareRemoteInput(rDevId, prepareCb);
    if (ret == SUCCESS) {
        std::cout << "PrepareRemoteInput is success! " << std::endl;
    } else {
        std::cout << "PrepareRemoteInput is fail! " << std::endl;
    }

    std::cout << "PrepareRemoteInput::end " << std::endl;
}

void DistributedInputTest::UnprepareRemoteInput()
{
    std::cout << "UnprepareRemoteInput::start" << std::endl;

    std::string rDevId = "";

    std::cout << "devId::";
    std::cin >> rDevId;

    UnprepareCb = new(std::nothrow) TestUnprepareDInputCallback();
    int32_t ret = DistributedInputKit::UnprepareRemoteInput(rDevId, UnprepareCb);
    if (ret == SUCCESS) {
        std::cout << "UnprepareRemoteInput is success! " << std::endl;
    } else {
        std::cout << "UnprepareRemoteInput is fail! " << std::endl;
    }

    std::cout << "UnprepareRemoteInput::end " << std::endl;
}

void DistributedInputTest::StartRemoteInput()
{
    std::cout << "StartRemoteInput::start " << std::endl;
    PrintDeviceTypeCmd();

    std::string rDevId = "";
    uint32_t rInputType = 0;

    std::cout << "devId::";
    std::cin >> rDevId;

    std::cout << "inputType::";
    std::cin >> rInputType;

    startCb = new(std::nothrow) TestStartDInputCallback();
    int32_t ret = DistributedInputKit::StartRemoteInput(rDevId, rInputType, startCb);
    if (ret == SUCCESS) {
        std::cout << "StartRemoteInput is success! " << std::endl;
    } else {
        std::cout << "StartRemoteInput is fail! " << std::endl;
    }

    std::cout << "StartRemoteInput::end " << std::endl;
}

void DistributedInputTest::StopRemoteInput()
{
    std::cout << "StopRemoteInput::start " << std::endl;
    PrintDeviceTypeCmd();

    std::string rDevId = "";
    uint32_t rInputType = 0;

    std::cout << "devId::";
    std::cin >> rDevId;

    std::cout << "inputType::";
    std::cin >> rInputType;

    stopCb = new(std::nothrow) TestStopDInputCallback();
    int32_t ret = DistributedInputKit::StopRemoteInput(rDevId, rInputType, stopCb);
    if (ret == SUCCESS) {
        std::cout << "StopRemoteInput is success! " << std::endl;
    } else {
        std::cout << "StopRemoteInput is fail! " << std::endl;
    }

    std::cout << "StopRemoteInput::end " << std::endl;
}

void DistributedInputTest::Help()
{
    std::cout << "***************************************************************" << std::endl;
    std::cout << "Welcome to use dinput source test demo!" << std::endl;
    std::cout << "1 ::GetDeviceInfo " << std::endl;
    std::cout << "2 ::PrepareRemoteInput " << std::endl;
    std::cout << "3 ::StartRemoteInput " << std::endl;
    std::cout << "4 ::StopRemoteInput " << std::endl;
    std::cout << "5 ::UnprepareRemoteInput " << std::endl;
    std::cout << "6 ::IsStartDistributedInput " << std::endl;
    std::cout << "7 ::IsNeedFilterOut " << std::endl;
    std::cout << "8 ::Help " << std::endl;
    std::cout << "0 ::Exit " << std::endl;
    std::cout << "***************************************************************" << std::endl;
}

void DistributedInputTest::SwitchCase(int32_t in)
{
    switch (in) {
        case TEST_GET_DEVICE_INFO:
            GetDeviceInfo();
            break;
        case TEST_PREPARE_REMOTE:
            PrepareRemoteInput();
            break;
        case TEST_START_REMOTE:
            StartRemoteInput();
            break;
        case TEST_STOP_REMOTE:
            StopRemoteInput();
            break;
        case TEST_UN_PREPARE_REMOTE:
            UnprepareRemoteInput();
            break;
        case TEST_IS_START_INPUT:
            IsStartDistributedInput();
            break;
        case TEST_IS_NEED_FILTER_OUT:
            IsNeedFilterOut();
            break;
        case TEST_HELP:
            Help();
            break;
        default:
            break;
    }
}
}
}
}

using namespace OHOS::DistributedHardware::DistributedInput;

int main()
{
    DistributedInputTest dInputTest;
    dInputTest.Help();
    int32_t in;
    while (true) {
        std::cout << std::endl;
        std::cout << "Enter the case No : ";
        std::cin >> in;
        if (in == 0) {
            break;
        }
        dInputTest.SwitchCase(in);
    }
}

